struct _StClickablePrivate {
gboolean active;
gboolean held;
- gboolean hover;
gboolean pressed;
guint initiating_button;
PROP_0,
PROP_ACTIVE,
- PROP_HOVER,
PROP_PRESSED,
};
st_widget_add_style_pseudo_class (ST_WIDGET (self), "pressed");
else
st_widget_remove_style_pseudo_class (ST_WIDGET (self), "pressed");
-
- if (self->priv->hover)
- st_widget_add_style_pseudo_class (ST_WIDGET (self), "hover");
- else
- st_widget_remove_style_pseudo_class (ST_WIDGET (self), "hover");
}
static void
g_object_notify (G_OBJECT (self), "active");
}
-static void
-set_hover (StClickable *self,
- gboolean hover)
-{
- if (self->priv->hover == hover)
- return;
- self->priv->hover = hover;
- sync_pseudo_class (self);
- g_object_notify (G_OBJECT (self), "hover");
-}
-
static void
set_pressed (StClickable *self,
gboolean pressed)
ClutterCrossingEvent *event)
{
StClickable *self = ST_CLICKABLE (actor);
-
- if (st_clickable_contains (self, event->related))
- return TRUE;
- if (!st_clickable_contains (self, event->source))
- return TRUE;
+ gboolean result;
g_object_freeze_notify (G_OBJECT (actor));
- if (self->priv->held)
- set_pressed (self, TRUE);
- set_hover (self, TRUE);
+ result = CLUTTER_ACTOR_CLASS (st_clickable_parent_class)->enter_event (actor, event);
+
+ /* We can't just assume get_hover() is TRUE; see st_widget_enter(). */
+ set_pressed (self, self->priv->held && st_widget_get_hover (ST_WIDGET (actor)));
g_object_thaw_notify (G_OBJECT (actor));
- return TRUE;
+ return result;
}
static gboolean
ClutterCrossingEvent *event)
{
StClickable *self = ST_CLICKABLE (actor);
+ gboolean result;
- if (st_clickable_contains (self, event->related))
- return TRUE;
+ g_object_freeze_notify (G_OBJECT (actor));
- set_hover (self, FALSE);
- set_pressed (self, FALSE);
+ result = CLUTTER_ACTOR_CLASS (st_clickable_parent_class)->leave_event (actor, event);
- return TRUE;
+ /* As above, we can't just assume get_hover() is FALSE. */
+ set_pressed (self, self->priv->held && st_widget_get_hover (ST_WIDGET (actor)));
+
+ g_object_thaw_notify (G_OBJECT (actor));
+
+ return result;
}
static gboolean
case PROP_PRESSED:
g_value_set_boolean (value, self->priv->pressed);
break;
- case PROP_HOVER:
- g_value_set_boolean (value, self->priv->hover);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
FALSE,
G_PARAM_READWRITE));
- /**
- * StClickable:hover
- *
- * This property tracks whether the mouse is over the button; note this
- * state is independent of whether the button is pressed.
- */
- g_object_class_install_property (gobject_class,
- PROP_HOVER,
- g_param_spec_boolean ("hover",
- "Hovering state",
- "Whether the mouse is over the button",
- FALSE,
- G_PARAM_READABLE));
-
/**
* StClickable:pressed
*
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ST_TYPE_CLICKABLE,
StClickablePrivate);
+ st_widget_set_track_hover (ST_WIDGET (self), TRUE);
}
gboolean is_style_dirty : 1;
gboolean draw_bg_color : 1;
gboolean draw_border_internal : 1;
+ gboolean track_hover : 1;
+ gboolean hover : 1;
StTooltip *tooltip;
PROP_PSEUDO_CLASS,
PROP_STYLE_CLASS,
PROP_STYLE,
-
PROP_STYLABLE,
-
PROP_HAS_TOOLTIP,
- PROP_TOOLTIP_TEXT
+ PROP_TOOLTIP_TEXT,
+ PROP_TRACK_HOVER,
+ PROP_HOVER
};
enum
st_widget_set_tooltip_text (actor, g_value_get_string (value));
break;
+ case PROP_TRACK_HOVER:
+ st_widget_set_track_hover (actor, g_value_get_boolean (value));
+ break;
+
+ case PROP_HOVER:
+ st_widget_set_hover (actor, g_value_get_boolean (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
g_value_set_string (value, st_widget_get_tooltip_text (actor));
break;
+ case PROP_TRACK_HOVER:
+ g_value_set_boolean (value, priv->track_hover);
+ break;
+
+ case PROP_HOVER:
+ g_value_set_boolean (value, priv->hover);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
return priv->theme_node;
}
+static gboolean
+actor_contains (ClutterActor *widget,
+ ClutterActor *other)
+{
+ while (other != NULL && other != widget)
+ other = clutter_actor_get_parent (other);
+ return other != NULL;
+}
+
static gboolean
st_widget_enter (ClutterActor *actor,
ClutterCrossingEvent *event)
{
StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
-
- if (priv->has_tooltip)
- st_widget_show_tooltip ((StWidget*) actor);
+ if (priv->track_hover)
+ {
+ if (actor_contains (actor, event->source))
+ st_widget_set_hover (ST_WIDGET (actor), TRUE);
+ else
+ {
+ /* The widget has a grab and is being told about an
+ * enter-event outside its hierarchy. Hopefully we already
+ * got a leave-event, but if not, handle it now.
+ */
+ st_widget_set_hover (ST_WIDGET (actor), FALSE);
+ }
+ }
if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event)
return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->enter_event (actor, event);
{
StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
- if (priv->has_tooltip)
- st_tooltip_hide (priv->tooltip);
+ if (priv->track_hover)
+ {
+ if (!actor_contains (actor, event->related))
+ st_widget_set_hover (ST_WIDGET (actor), FALSE);
+ }
if (CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event)
return CLUTTER_ACTOR_CLASS (st_widget_parent_class)->leave_event (actor, event);
/**
* StWidget:has-tooltip:
*
- * Determines whether the widget has a tooltip. If set to TRUE, causes the
- * widget to monitor enter and leave events (i.e. sets the widget reactive).
+ * Determines whether the widget has a tooltip. If set to %TRUE, causes the
+ * widget to monitor hover state (i.e. sets #ClutterActor:reactive and
+ * #StWidget:track-hover).
*/
pspec = g_param_spec_boolean ("has-tooltip",
"Has Tooltip",
ST_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TOOLTIP_TEXT, pspec);
+ /**
+ * StWidget:track-hover:
+ *
+ * Determines whether the widget tracks pointer hover state. If
+ * %TRUE (and the widget is visible and reactive), the
+ * #StWidget:hover property and "hover" style pseudo class will be
+ * adjusted automatically as the pointer moves in and out of the
+ * widget.
+ */
+ pspec = g_param_spec_boolean ("track-hover",
+ "Track hover",
+ "Determines whether the widget tracks hover state",
+ FALSE,
+ ST_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_TRACK_HOVER,
+ pspec);
+
+ /**
+ * StWidget:hover:
+ *
+ * Whether or not the pointer is currently hovering over the widget. This is
+ * only tracked automatically if #StWidget:track-hover is %TRUE, but you can
+ * adjust it manually in any case.
+ */
+ pspec = g_param_spec_boolean ("hover",
+ "Hover",
+ "Whether the pointer is hovering over the widget",
+ FALSE,
+ ST_PARAM_READWRITE);
+ g_object_class_install_property (gobject_class,
+ PROP_HOVER,
+ pspec);
+
/**
* StWidget::style-changed:
*
*
* Enables tooltip support on the #StWidget.
*
- * Note that setting has-tooltip to %TRUE will cause the widget to be set
- * reactive. If you no longer need tooltip support and do not need the widget
- * to be reactive, you need to set ClutterActor::reactive to FALSE.
- *
+ * Note that setting has-tooltip to %TRUE will cause
+ * #ClutterActor:reactive and #StWidget:track-hover to be set %TRUE as
+ * well, but you must clear these flags yourself (if appropriate) when
+ * setting it %FALSE.
*/
void
st_widget_set_has_tooltip (StWidget *widget,
if (has_tooltip)
{
clutter_actor_set_reactive ((ClutterActor*) widget, TRUE);
+ st_widget_set_track_hover (widget, TRUE);
if (!priv->tooltip)
{
if (widget->priv->tooltip)
st_tooltip_hide (widget->priv->tooltip);
}
+
+/**
+ * st_widget_set_track_hover:
+ * @widget: A #StWidget
+ * @track_hover: %TRUE if the widget should track the pointer hover state
+ *
+ * Enables hover tracking on the #StWidget.
+ *
+ * If hover tracking is enabled, and the widget is visible and
+ * reactive, then @widget's #StWidget:hover property will be updated
+ * automatically to reflect whether the pointer is in @widget (or one
+ * of its children), and @widget's #StWidget:pseudo-class will have
+ * the "hover" class added and removed from it accordingly.
+ *
+ * Note that currently it is not possible to correctly track the hover
+ * state when another actor has a pointer grab. You can use
+ * st_widget_sync_hover() to update the property manually in this
+ * case.
+ */
+void
+st_widget_set_track_hover (StWidget *widget,
+ gboolean track_hover)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = widget->priv;
+
+ if (priv->track_hover != track_hover)
+ {
+ priv->track_hover = track_hover;
+ g_object_notify (G_OBJECT (widget), "track-hover");
+
+ if (priv->track_hover)
+ st_widget_sync_hover (widget);
+ }
+}
+
+/**
+ * st_widget_get_track_hover:
+ * @widget: A #StWidget
+ *
+ * Returns the current value of the track-hover property. See
+ * st_tooltip_set_track_hover() for more information.
+ *
+ * Returns: current value of track-hover on @widget
+ */
+gboolean
+st_widget_get_track_hover (StWidget *widget)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+ return widget->priv->track_hover;
+}
+
+/**
+ * st_widget_set_hover:
+ * @widget: A #StWidget
+ * @hover: whether the pointer is hovering over the widget
+ *
+ * Sets @widget's hover property and adds or removes "hover" from its
+ * pseudo class accordingly. If #StWidget:has-tooltip is %TRUE, this
+ * will also show or hide the tooltip, as appropriate.
+ *
+ * If you have set #StWidget:track-hover, you should not need to call
+ * this directly. You can call st_widget_sync_hover() if the hover
+ * state might be out of sync due to another actor's pointer grab.
+ */
+void
+st_widget_set_hover (StWidget *widget,
+ gboolean hover)
+{
+ StWidgetPrivate *priv;
+
+ g_return_if_fail (ST_IS_WIDGET (widget));
+
+ priv = widget->priv;
+
+ if (priv->hover != hover)
+ {
+ priv->hover = hover;
+ if (priv->hover)
+ {
+ st_widget_add_style_pseudo_class (widget, "hover");
+ if (priv->has_tooltip)
+ st_widget_show_tooltip (widget);
+ }
+ else
+ {
+ st_widget_remove_style_pseudo_class (widget, "hover");
+ if (priv->has_tooltip)
+ st_widget_hide_tooltip (widget);
+ }
+ g_object_notify (G_OBJECT (widget), "hover");
+ }
+}
+
+/**
+ * st_widget_sync_hover:
+ * @widget: A #StWidget
+ *
+ * Sets @widget's hover state according to the current pointer
+ * position. This can be used to ensure that it is correct after
+ * (or during) a pointer grab.
+ */
+void
+st_widget_sync_hover (StWidget *widget)
+{
+ ClutterDeviceManager *device_manager;
+ ClutterInputDevice *pointer;
+ ClutterActor *actor;
+
+ device_manager = clutter_device_manager_get_default ();
+ pointer = clutter_device_manager_get_core_device (device_manager,
+ CLUTTER_POINTER_DEVICE);
+ actor = clutter_input_device_get_pointer_actor (pointer);
+
+ while (actor && actor != (ClutterActor *)widget)
+ actor = clutter_actor_get_parent (actor);
+
+ st_widget_set_hover (widget, actor == (ClutterActor *)widget);
+}
+
+/**
+ * st_widget_get_hover:
+ * @widget: A #StWidget
+ *
+ * If #StWidget:track-hover is set, this returns whether the pointer
+ * is currently over the widget.
+ *
+ * Returns: current value of hover on @widget
+ */
+gboolean
+st_widget_get_hover (StWidget *widget)
+{
+ g_return_val_if_fail (ST_IS_WIDGET (widget), FALSE);
+
+ return widget->priv->hover;
+}